/* PDumper dynamic Memory Management */
/* Created 09-23-2003 T. Milius Adapted from older source of my libraries */
/* Changed 07-19-2005 T. Milius */
/* (c) Copyright 2003-2005 by Thomas Milius Stade, Germany
   Source must not be altered without agreement of the owner.
   The owner of the source is allowed to use this code inside programs without
   publishing the code of this programs. These programs may be commercial.
   Other developers can use this source freely inside own software if the source
   code of this programs is made public to same conditions like valid to this code
   and no commercial profit is taken from the programs based on this code

   Code or parts of it are not allowed to be used within GPL code or
   similar licenses which are "infecting" other code and trying to "supersede"
   other licenses. */
/* RISCOS */

#ifndef mem_manag_h
#define mem_manag_h

/* !!!!!!!!!! libraries !!!!!!!!!! */
/* ---------- ANSI-C ---------- */
#include <stddef.h>
#include <stdlib.h>

/* ---------- RISCOS ---------- */
#include <kernel.h>
#include <swis.h>

/* !!!!!!!!!! definitions !!!!!!!!!! */
/* to clarify reading */
#ifndef FALSE
#define FALSE 0
#endif

#ifndef TRUE
#define TRUE 1
#endif

#define DUMPER_DYNAMIC_AREA_NAME "PDumperEI"

#define DYNAMIC_AREA_HEAP_HEADER_SIZE 256
#define DYNAMIC_AREA_EXPANSION_SIZE 524288

/* !!!!!!!!!! data structures !!!!!!!!!! */

/* !!!!!!!!!! support functions !!!!!!!!!! */

/* !!!!!!!!!! functions !!!!!!!!!! */
void dumper_free(int dynamic_area_no,
                 void *ptr)
{
_kernel_swi_regs regs;

if (dynamic_area_no >= 0) {
  regs.r[0]=2;
  regs.r[1]=dynamic_area_no;
  _kernel_swi(OS_DynamicArea, &regs, &regs);
  regs.r[1]=regs.r[3];
  regs.r[0]=3;
  regs.r[2]=(int) ((unsigned long) ptr - 4);
  _kernel_swi(OS_Heap, &regs, &regs);
  }
else {
  free(ptr);
  }
}

void *dumper_malloc(int dynamic_area_no,
                    size_t size)
{
_kernel_swi_regs regs;
int heap_base;
int *requested_size;

if (dynamic_area_no >= 0) {
  /* Internal always a multiple of 4 */
  if ((size%4) != 0) {
    size+=(4 - (size%4));
    }
  /* Exit by certain situations later */
  while (1 == 1) {
    regs.r[0]=2;
    regs.r[1]=dynamic_area_no;
    _kernel_swi(OS_DynamicArea, &regs, &regs);
    heap_base=regs.r[3];
    regs.r[1]=heap_base;
    regs.r[0]=2;
    /* 4 additional bytes are required to store the size
       for allowing realloc */
    regs.r[3]=size + 4;
    if (_kernel_swi(OS_Heap, &regs, &regs) == NULL) {
      requested_size=(int *) regs.r[2];
      *requested_size=size;
      return (void *) ((unsigned long) regs.r[2] + 4);
      }
    else {
      /* Try to expand dynamic area:
         Note that Heap overhead can be big.
         So expand in given steps while maximum of area is not reached
         already */
      regs.r[0]=dynamic_area_no;
      regs.r[1]=DYNAMIC_AREA_EXPANSION_SIZE;
      _kernel_swi(OS_ChangeDynamicArea, &regs, &regs);
      if (regs.r[1] == 0) {
        return NULL;
        }
      /* Expand Heap */
      regs.r[3]=regs.r[1];
      regs.r[0]=5;
      regs.r[1]=heap_base;
      _kernel_swi(OS_Heap, &regs, &regs);
      }
    }
  }
else {
  return malloc(size);
  }
}

void *dumper_realloc(int dynamic_area_no,
                     void *ptr,
                     size_t size)
{
_kernel_swi_regs regs;
int heap_base;
int *old_size;

if (dynamic_area_no >= 0) {
  if (!ptr) {
    return dumper_malloc(dynamic_area_no,
                         size);
    }
  if (size == 0) {
    dumper_free(dynamic_area_no,
                ptr);
    return ptr;
    }
  /* Internal always a multiple of 4 */
  if ((size%4) != 0) {
    size+=(4 - (size%4));
    }
  old_size=(int *) ((unsigned long) ptr - 4);
  if ((size - *old_size) == 0) {
    /* No change */
    return ptr;
    }
  /* Exit by certain situations later */
  while (1 == 1) {
    regs.r[0]=2;
    regs.r[1]=dynamic_area_no;
    _kernel_swi(OS_DynamicArea, &regs, &regs);
    heap_base=regs.r[3];
    regs.r[1]=heap_base;
    regs.r[0]=4;
    regs.r[2]=(int) old_size;
    /* Because determining byte change there is no need
       to worry about 4 Byte size storage */
    regs.r[3]=size - *old_size;
    if (_kernel_swi(OS_Heap, &regs, &regs) == NULL) {
      old_size=(int *) regs.r[2];
      *old_size=size;
      return (void *) ((unsigned long) regs.r[2] + 4);
      }
    else {
      /* Try to expand dynamic area:
         Note that Heap overhead can be big.
         So expand in given steps while maximum of area is not reached
         already */
      regs.r[0]=dynamic_area_no;
      regs.r[1]=DYNAMIC_AREA_EXPANSION_SIZE;
      _kernel_swi(OS_ChangeDynamicArea, &regs, &regs);
      if (regs.r[1] == 0) {
        return NULL;
        }
      /* Expand Heap */
      regs.r[3]=regs.r[1];
      regs.r[0]=5;
      regs.r[1]=heap_base;
      _kernel_swi(OS_Heap, &regs, &regs);
      }
    }
  }
else {
  return realloc(ptr,
                 size);
  }
}

int dumper_dynamic_area_initialize(long max_area_size)
{
_kernel_swi_regs regs;
int new_area_number;
void *base_of_area;

/* test for dynamic area support */
regs.r[0]=3;
regs.r[1]=-1;
if (_kernel_swi(OS_DynamicArea, &regs, &regs) != NULL) {
  /* No Dynamic Area so take RMA for memory allocation */
  return -1;
  }
regs.r[0]=0;
regs.r[1]=-1;
regs.r[2]=DYNAMIC_AREA_EXPANSION_SIZE;
regs.r[3]=-1;
regs.r[4]=0x00000080;
regs.r[5]=max_area_size;
regs.r[6]=NULL;
regs.r[7]=NULL;
regs.r[8]=(int) DUMPER_DYNAMIC_AREA_NAME;
_kernel_swi(OS_DynamicArea, &regs, &regs);
new_area_number=regs.r[1];
base_of_area=(void *) regs.r[3];
/* Determine true actual size */
regs.r[0]=2;
_kernel_swi(OS_DynamicArea, &regs, &regs);
/* Initialize the Heap */
regs.r[0]=0;
regs.r[1]=(int) base_of_area;
regs.r[3]=regs.r[2] - DYNAMIC_AREA_HEAP_HEADER_SIZE;
_kernel_swi(OS_Heap, &regs, &regs);
return new_area_number;
}

int dumper_dynamic_area_release(int dynamic_area_no)
{
_kernel_swi_regs regs;

if (dynamic_area_no < 0) return TRUE;
/* No Need to release the heap for it is dropped with the area */
/* Release the Area */
regs.r[0]=1;
regs.r[1]=dynamic_area_no;
_kernel_swi(OS_DynamicArea, &regs, &regs);
return TRUE;
}

#endif
